<html>
<title>Text-to-Speech on the Web</title>
<head>
  <script>
    // This demo is licensed under the GNU GPL.

    var print = console.log;

    var Module = {
      arguments: ['-w', 'wav.wav', '-v', 'en/en-us', '--path=/espeak'],
      noInitialRun: true
    };
  </script>
  <script src="espeak.js"></script>
  <script>
    // expects the webserver to run in the parent of espeak-data/

    FS.createPath('/', 'espeak/espeak-data', true, false);
    ['phontab', 'phonindex', 'phondata', 'intonations', 'en_dict'].forEach(function(datafile) {
      FS.createLazyFile('/espeak/espeak-data', datafile, 'espeak-data/' + datafile, true, false);
    });

    FS.createPath('/', 'espeak/espeak-data/voices/en', true, false);
    FS.createLazyFile('/espeak/espeak-data/voices/en', 'en-us', 'espeak-data/voices/en/en-us', true, false);

    FS.root.write = true;

    function talk(text) {
      Module.arguments.push(text);
      run();
      Module.arguments.pop();

      var wav = FS.root.contents['wav.wav'].contents;

      function encode64(data) {
        var BASE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
        var PAD = '=';
        var ret = '';
        var leftchar = 0;
        var leftbits = 0;
        for (var i = 0; i < data.length; i++) {
          leftchar = (leftchar << 8) | data[i];
          leftbits += 8;
          while (leftbits >= 6) {
            var curr = (leftchar >> (leftbits-6)) & 0x3f;
            leftbits -= 6;
            ret += BASE[curr];
          }
        }
        if (leftbits == 2) {
          ret += BASE[(leftchar&3) << 4];
          ret += PAD + PAD;
        } else if (leftbits == 4) {
          ret += BASE[(leftchar&0xf) << 2];
          ret += PAD;
        }
        return ret;
      }

      for (var i = 0; i < wav.length; i++)
        wav[i] = unSign(wav[i], 8);

      document.getElementById("audio").innerHTML=("<audio id=\"player\" src=\"data:audio/x-wav;base64,"+encode64(wav)+"\">");
      document.getElementById("player").play();
    }
  </script>
</head>
<body>
  <h1>Text-To-Speech on the Web</h1>
  <form onsubmit="talk(text.value); return false">
    Text: <input type="text" name="text" size=50 value="Never gonna give, you, up."><input type="submit" value="Go!">
  </form>
  <hr>
  <p>
    This demo is 100% clientside JavaScript. It uses <a href="http://espeak.sourceforge.net/">eSpeak</a>, an open source
    speech synthesizer, which was compiled from C++ to JavaScript using <a href="http://emscripten.org">Emscripten</a>.
    Source code for this demo can be found <a href="espeak_src.tar.bz2">here</a>.
  </p>
  <p>
    Browser requirements:
    <ul>
      <li><b>Typed arrays</b>. The eSpeak code is not portable to the extent that would be necessary to avoid using typed arrays.
          (It should however be possible to rewrite small bits of eSpeak to fix that.)
          This is present in Firefox and Chrome, but not IE, Safari or Opera.</li>
      <li><b>Support for WAV audio in data URIs</b>. eSpeak's simplest form of output is a WAV file, and the easiest way to use
          that is via a data URI. (It should however be possible to extract the raw audio directly by hacking eSpeak.)
          This is present in Firefox, Safari and Opera, but not IE or Chrome.</li>
    </ul>
    So currently this demo will only work in Firefox. Help is welcome regarding the workarounds mentioned above
    that will let it work elsewhere, we would like to develop this demo into a useful project for people to use around the web.
  </p>
  <div id="audio"></div>
</body>
</html>

